import ast
import time
import traceback
from datetime import datetime, timedelta
from functools import partial
from typing import Optional

from fastapi import Depends, HTTPException, Security, status
from fastapi.security import OAuth2PasswordBearer, SecurityScopes, HTTPBearer
from jose import JWTError, jwt
from passlib.context import CryptContext
from pydantic import ValidationError
from sqlalchemy.orm import Session

from api.user.serializers import UserInDB, TokenData, UserRole
from core.db.database import get_db
from core.models import models
from core.models.models import User
from env import setting

# 使用环境配置的密钥和算法
SECRET_KEY = setting.JWT_SECRET_KEY
ALGORITHM = setting.JWT_ALGORITHM
ACCESS_TOKEN_EXPIRE_MINUTES = setting.JWT_ACCESS_TOKEN_EXPIRE_MINUTES

# 定义角色的常量
SUPER_ADMIN = "super_admin"
ADMIN = "admin"
USER = "user"

# 移除未使用的假用户数据库
pwd_context = CryptContext(schemes=["bcrypt"], deprecated="auto")

oauth2_scheme = OAuth2PasswordBearer(
    tokenUrl="/api/token",
    scopes={"super_admin": "super_admin", "admin": "admin", "user": "user"},
)


def verify_password(plain_password, hashed_password):
    return pwd_context.verify(plain_password, hashed_password)


def get_password_hash(password):
    return pwd_context.hash(password)


def get_user_in_db(username: str, db: Session = None):
    """从数据库获取用户

    Args:
        username (str): 用户名
        db (Session, optional): 数据库会话。如果未提供，将创建新会话。

    Returns:
        User: 找到的用户对象，如果未找到则返回None
    """
    close_session = False
    if db is None:
        db = next(get_db())
        close_session = True
    
    try:
        return db.query(models.User).filter(models.User.userName == username).first()
    finally:
        if close_session:
            db.close()


def authenticate_user(username: str, password: str, db: Session = None):
    """验证用户凭据

    Args:
        username (str): 用户名
        password (str): 密码
        db (Session, optional): 数据库会话

    Returns:
        User|bool: 验证成功返回用户对象，否则返回False
    """
    user = get_user_in_db(username=username, db=db)
    if not user:
        return False
    if not verify_password(password, user.userPwd):
        return False
    return user


def create_access_token(data: dict, expires_delta: timedelta | None = None):
    """创建JWT访问令牌

    Args:
        data (dict): 要编码到令牌中的数据
        expires_delta (timedelta, optional): 令牌过期时间。如果未提供，使用默认值。

    Returns:
        str: 编码后的JWT令牌
    """
    to_encode = data.copy()
    if expires_delta:
        expire = datetime.utcnow() + expires_delta
    else:
        expire = datetime.utcnow() + timedelta(minutes=ACCESS_TOKEN_EXPIRE_MINUTES)
    to_encode.update({"exp": expire})
    encoded_jwt = jwt.encode(to_encode, SECRET_KEY, algorithm=ALGORITHM)
    return encoded_jwt


async def get_current_user(
        security_scopes: SecurityScopes, token: str = Depends(oauth2_scheme)
):
    """获取当前认证用户

    Args:
        security_scopes (SecurityScopes): 安全作用域
        token (str, optional): JWT令牌。默认从OAuth2方案获取。

    Returns:
        User: 当前认证用户
        
    Raises:
        HTTPException: 如果令牌无效或用户无权访问
    """
    if security_scopes.scopes:
        authenticate_value = f'Bearer scope="{security_scopes.scope_str}"'
    else:
        authenticate_value = "Bearer"
    credentials_exception = HTTPException(
        status_code=status.HTTP_401_UNAUTHORIZED,
        detail="Signature has expired.",
        headers={"WWW-Authenticate": authenticate_value},
    )
    try:
        payload = jwt.decode(token, SECRET_KEY, algorithms=[ALGORITHM])
        username: str = payload.get("sub")
        if username is None:
            raise credentials_exception
        token_scopes = payload.get("scopes", [])
        token_data = TokenData(scopes=token_scopes, username=username)
    except (JWTError, ValidationError) as e:
        traceback.print_exc()
        raise credentials_exception
    
    user = get_user_in_db(username=username)
    if user is None:
        raise credentials_exception

    if user.isActive == "0":
        raise HTTPException(status_code=400, detail="Inactive user")

    return user


def has_permission(allowed_roles: list[UserRole], user: User = Depends(get_current_user)):
    """检查用户是否有权限

    Args:
        allowed_roles (list[UserRole]): 允许的角色列表
        user (User, optional): 用户对象。默认从依赖项获取。

    Returns:
        bool: 如果用户有权限，则返回True
        
    Raises:
        HTTPException: 如果用户没有权限
    """
    if user.role[0].name == UserRole.super_admin:
        return True
    if UserRole.admin in allowed_roles and user.role[0].name == UserRole.admin:
        return True
    if UserRole.user in allowed_roles and user.role[0].name == UserRole.user:
        return True
    raise HTTPException(status_code=status.HTTP_403_FORBIDDEN, detail="Not enough permissions")


def get_current_user_mid(token: str) -> Optional[User]:
    """从令牌中获取用户（用于中间件）

    Args:
        token (str): JWT令牌

    Returns:
        Optional[User]: 用户对象，如果令牌无效则返回None
    """
    try:
        payload = jwt.decode(token, SECRET_KEY, algorithms=[ALGORITHM])
        username = payload.get("sub", None)
        user = get_user_in_db(username=username)
        return user
    except:
        return None


class Permission(object):
    """权限辅助类，简化权限检查"""
    
    def __init__(self):
        self.super_admin_permission = partial(has_permission, allowed_roles=[UserRole.super_admin])
        self.admin_permission = partial(has_permission, allowed_roles=[UserRole.super_admin, UserRole.admin])
        self.user_permission = partial(has_permission,
                                       allowed_roles=[UserRole.super_admin, UserRole.admin, UserRole.user])


permission = Permission()
